package com.aplink.generic.util;

import android.os.Environment;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Calendar;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class SimpleCrypto {

    private static String encryptedFileName = "sellproof.ck";
    private final static String HEX = "0123456789ABCDEF";

    private static String seedUserFrequency = "APLINK";

    private static void appendHex( StringBuffer sb, byte b ) {
        sb.append( HEX.charAt( ( b >> 4 ) & 0x0f ) ).append( HEX.charAt( b & 0x0f ) );
    }

    private static byte[] decrypt( byte[] raw, byte[] encrypted )
            throws Exception {
        final SecretKeySpec skeySpec = new SecretKeySpec( raw, "AES" );
        final Cipher cipher = Cipher.getInstance( "AES" );
        cipher.init( Cipher.DECRYPT_MODE, skeySpec );
        return cipher.doFinal( encrypted );
    }

    private static byte[] encrypt( byte[] raw, byte[] clear ) throws Exception {
        final SecretKeySpec skeySpec = new SecretKeySpec( raw, "AES" );
        final Cipher cipher = Cipher.getInstance( "AES" );
        cipher.init( Cipher.ENCRYPT_MODE, skeySpec );
        return cipher.doFinal( clear );
    }

    private static boolean encryptRawDataIntoFile( StringBuilder rawData ) {
        boolean isSuccessful = false;

        if ( rawData == null ) {
            return false;
        }

        try {
            if ( ! Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED ) ) {
                // handle case of no SDCARD present
            } else {
                final File encryptedFile = new File(
                        Environment.getExternalStorageDirectory()
                                + File.separator
                                + "SellProof" // folder name
                                + File.separator
                                + SimpleCrypto.encryptedFileName ); // file name

                /***
                 * we must create the new file in this folder in any case
                 */

                if ( ! encryptedFile.exists() ) {
                    encryptedFile.createNewFile(); // create the file
                } else {
                    encryptedFile.delete();
                    encryptedFile.createNewFile(); // create the file
                }

                final FileOutputStream fos = new FileOutputStream( encryptedFile );

                // firstly we encrypt the data
                /***
                 * We must write encrypt content and save in theFile
                 */
                // getRawKey from seed
                final byte[] rawKey = getRawKey( SimpleCrypto.seedUserFrequency
                        .getBytes() );
                final SecretKeySpec sks = new SecretKeySpec( rawKey, "AES" );
                // create cipher
                final Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
                final IvParameterSpec ivParameterSpec = new IvParameterSpec( rawKey );
                cipher.init( Cipher.ENCRYPT_MODE, sks, ivParameterSpec );

                // create the inputstream of clearContent
                // convert String into InputStream
                final InputStream is = new ByteArrayInputStream( rawData.toString()
                        .getBytes() );

                // Wrap the output stream
                final CipherOutputStream cos = new CipherOutputStream( fos, cipher );
                // Write bytes
                int b;
                final byte[] d = new byte[8];
                while ( ( b = is.read( d ) ) != - 1 ) {
                    cos.write( d, 0, b );
                }
                cos.flush();

                if ( cos != null ) {
                    cos.close();
                }
                if ( fos != null ) {
                    fos.close();
                }
                if ( is != null ) {
                    is.close();
                }

            }

        } catch ( final Exception e ) {
            e.printStackTrace();
            isSuccessful = false;
        }

        return isSuccessful;
    }

    private static byte[] getRawKey( byte[] seed ) throws Exception {
        final KeyGenerator kgen = KeyGenerator.getInstance( "AES" );
        final SecureRandom sr = SecureRandom.getInstance( "SHA1PRNG" );
        sr.setSeed( seed );
        kgen.init( 128, sr ); // 192 and 256 bits may not be available
        final SecretKey skey = kgen.generateKey();
        final byte[] raw = skey.getEncoded();
        return raw;
    }

    public static boolean activeUserLog( String userId ) {

        boolean isSuccessful = false;

        StringBuilder clearContent = new StringBuilder();
        clearContent = SimpleCrypto.getRawDataFromEncryptedFile();

        if ( clearContent == null ) {
            return false;
        }

        // we append new data in raw file
        final Calendar currentCal = Calendar.getInstance();
        final int newyear = currentCal.get( Calendar.YEAR );
        final int newmonth = currentCal.get( Calendar.MONTH ) + 1;
        final int newday = currentCal.get( Calendar.DAY_OF_MONTH );
        final int newhourofday = currentCal.get( Calendar.HOUR_OF_DAY );
        final int newminute = currentCal.get( Calendar.MINUTE );
        final int newmilisecond = currentCal.get( Calendar.SECOND );

        // finally we encrypt it again and write back into the file
        final String newRecord = String.valueOf( newmonth ) + "-"
                + String.valueOf( newday ) + "-" + String.valueOf( newyear ) + " "
                + String.valueOf( newhourofday ) + ":"
                + String.valueOf( newminute ) + ":"
                + String.valueOf( newmilisecond ) + "/" + userId + ";";

        clearContent.append( newRecord );

        isSuccessful = encryptRawDataIntoFile( clearContent );

        // to pull data from sdcard into ws
        return isSuccessful;

    }

    public static String decrypt( String seed, String encrypted )
            throws Exception {
        final byte[] rawKey = getRawKey( seed.getBytes() );
        final byte[] enc = toByte( encrypted );
        final byte[] result = decrypt( rawKey, enc );
        return new String( result );
    }

    public static String encrypt( String seed, String cleartext )
            throws Exception {
        final byte[] rawKey = getRawKey( seed.getBytes() );
        final byte[] result = encrypt( rawKey, cleartext.getBytes() );
        return toHex( result );
    }

    public static String fromHex( String hex ) {
        return new String( toByte( hex ) );
    }

    public static StringBuilder getRawDataFromEncryptedFile() {

        StringBuilder clearContent = new StringBuilder();
        try {
            if ( ! Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED ) ) {
                // handle case of no SDCARD present
            } else {
                final File encryptedFile = new File(
                        Environment.getExternalStorageDirectory()
                                + File.separator
                                + "SellProof" // folder name
                                + File.separator
                                + SimpleCrypto.encryptedFileName ); // file name

                // In the first time, the encrypted file doesn't exist

                /***
                 * we must create the file in this folder
                 */

                if ( ! encryptedFile.exists() ) {
                    encryptedFile.getParentFile().mkdir();
                    encryptedFile.createNewFile(); // create the file
                    return clearContent;
                }

                final Calendar cal = Calendar.getInstance(); // this code is to create
                // an unique file name
                final String idString = String.valueOf( cal.getTimeInMillis() );

                final File decryptedFile = new File(
                        Environment.getExternalStorageDirectory()
                                + File.separator + "SellProof" // folder name
                                + File.separator + idString + ".ck" ); // file
                // name
                // we create the new file
                decryptedFile.createNewFile(); // create the file

                /***
                 * Eventually, we must read the encrypted content and decrypt
                 * them into clear text and save in decryptedFile
                 */
                // getRawKey from seed
                final byte[] rawKey = getRawKey( SimpleCrypto.seedUserFrequency
                        .getBytes() );
                final SecretKeySpec sks = new SecretKeySpec( rawKey, "AES" );
                // create cipher
                final Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
                final IvParameterSpec ivParameterSpec = new IvParameterSpec( rawKey );
                cipher.init( Cipher.DECRYPT_MODE, sks, ivParameterSpec );

                final FileInputStream fis = new FileInputStream( encryptedFile );

                final FileOutputStream fos = new FileOutputStream( decryptedFile );

                // decrypt the file
                final CipherInputStream cis = new CipherInputStream( fis, cipher );
                int b;
                final byte[] d = new byte[8];
                while ( ( b = cis.read( d ) ) != - 1 ) {
                    // write it into the output
                    fos.write( d, 0, b );
                }

                fos.flush();
                fos.close();
                cis.close();
                fis.close();

                // we read a gain the clear file and then delete the file
                final FileInputStream fcis = new FileInputStream( decryptedFile );
                final BufferedReader buf = new BufferedReader( new InputStreamReader(
                        fcis ) );
                String readString = new String();
                // just reading each line
                while ( ( readString = buf.readLine() ) != null ) {
                    clearContent.append( readString );
                    System.out.print( "<<<<<<<<<< Clear data:"
                            + clearContent.toString() );
                }

                buf.close();
                fcis.close();
                decryptedFile.delete(); // delete the file

                // finally return the String of clear data
            }

        } catch ( final Exception e ) {
            e.printStackTrace();
            clearContent = null;
        }

        return clearContent;

    }

    public static ArrayList<String> getUserLog( String contactId ) {

        final ArrayList<String> finalResult = new ArrayList<String>();

        // we firstly and fetch into the String builder to get raw data
        StringBuilder clearContent = new StringBuilder( "" );
        clearContent = SimpleCrypto.getRawDataFromEncryptedFile();

        if ( ( clearContent != null ) && ( clearContent.toString().compareTo( "" ) != 0 ) ) {

            final String[] subUsers = clearContent.toString().split( ";" );
            // secondly, we check with contactId
            for ( final String subUser : subUsers ) {
                if ( subUser.contains( contactId ) == true ) {
                    finalResult.add( subUser );
                }
            }
        }

        // delete the file
        final File encryptedFile = new File( Environment.getExternalStorageDirectory()
                + File.separator + "SellProof" // folder name
                + File.separator + SimpleCrypto.encryptedFileName ); // file name

        if ( encryptedFile.exists() ) {
            encryptedFile.delete();
        }

        return finalResult;
    }

    public static byte[] toByte( String hexString ) {
        final int len = hexString.length() / 2;
        final byte[] result = new byte[len];
        for ( int i = 0 ; i < len ; i++ ) {
            result[i] = Integer.valueOf( hexString.substring( 2 * i, ( 2 * i ) + 2 ),
                    16 ).byteValue();
        }
        return result;
    }

    public static String toHex( byte[] buf ) {
        if ( buf == null ) {
            return "";
        }
        final StringBuffer result = new StringBuffer( 2 * buf.length );
        for ( final byte element : buf ) {
            appendHex( result, element );
        }
        return result.toString();
    }

    public static String toHex( String txt ) {
        return toHex( txt.getBytes() );
    }

}
